package org.jeecg.common.util.easyexcel;

import cn.hutool.core.collection.CollUtil;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.enums.WriteDirectionEnum;
import com.alibaba.excel.write.builder.ExcelWriterSheetBuilder;
import com.alibaba.excel.write.merge.OnceAbsoluteMergeStrategy;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
import org.jeecg.common.util.DownloadUtil;
import org.jeecg.common.util.easyexcel.entity.ExportSheetConfig;
import org.jeecg.common.util.easyexcel.find_merge_range.MergeRangeFinder;
import org.jeecg.common.util.easyexcel.find_merge_range.Range;
import org.jeecg.common.util.easyexcel.strategy.CustomCellStyleStrategy;
import org.jeecg.common.util.easyexcel.strategy.ExcelWidthStyleStrategy;
import lombok.Cleanup;
import lombok.NonNull;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.poi.ss.usermodel.Workbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Component;
import org.springframework.util.Assert;

import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * easyexcel写工具类
 *
 * @author zjarlin
 * @since 2023/04/30
 */
@Slf4j
@Component
public class EasyExcelWriteUtil {
    private static HttpServletResponse response;
    private static HttpServletRequest httpServletRequest;

    /**
     * 模板填充
     *
     * @param response     响应
     * @param vo           签证官
     * @param templatePath 模板路径
     * @param fileName     文件名称 入参
     * @author addzero
     * @since 2022/11/27
     */
    @SneakyThrows(Exception.class)
    public static <VO> void templateFill(@NonNull HttpServletResponse response, VO vo, String templatePath, @NonNull String fileName) {
        templateFill(response, vo, null, templatePath, fileName);
    }

    /**
     * 模板填充
     *
     * @param response     响应
     * @param vo           一维字段填充
     * @param dtos         dto 二维列表填充
     * @param templatePath 模板路径
     * @param fileName     文件名称 入参
     * @author addzero
     * @since 2022/11/27
     */
    @SneakyThrows(Exception.class)
    public static <VO, DTO> void templateFill(HttpServletResponse response, VO vo, List<DTO> dtos, String templatePath, @NonNull String fileName) {

        ServletOutputStream out = response.getOutputStream();
        response.setContentType("multipart/form-data");
        response.setCharacterEncoding("UTF-8");

        response.setHeader("Content-disposition", "attachment;filename=" + fileName);
        //文件模板输入流
        InputStream inputStream = new ClassPathResource(templatePath).getInputStream();

        @Cleanup ExcelWriter writer = EasyExcel.write(out)
                .withTemplate(inputStream)
                //.registerConverter(new LocalDateConverter())
                //.registerConverter(new LocalDateTimeConverter())
                .build();

        //3.4 设置强制计算公式：不然公式会以字符串的形式显示在excel中
        Workbook workbook = writer.writeContext().writeWorkbookHolder().getWorkbook();
        workbook.setForceFormulaRecalculation(true);

        WriteSheet sheet = EasyExcel.writerSheet(0).build();
        //填充列表开启自动换行,自动换行表示每次写入一条list数据是都会重新生成一行空行,此选项默认是关闭的,需要提前设置为true
        FillConfig fillConfig = FillConfig.builder()
                .forceNewRow(true)
                .direction(WriteDirectionEnum.VERTICAL)
                .build();
        //填充一维数据
        writer.fill(vo, sheet);
        //填充二维数据
        if (CollUtil.isNotEmpty(dtos)) {
            writer.fill(dtos, fillConfig, sheet);
        }
        //填充完成
        writer.finish();
        out.flush();
    }

    /**
     * 列表导出下载
     *
     * @param list     列表
     * @param fileName 文件名称 入参
     * @author addzero
     * @since 2022/11/27
     */
    @SneakyThrows
    public static <T> void listExport(List<T> list, @NonNull String fileName) {
        Objects.requireNonNull(list, "export list must not be null");
        Assert.notEmpty(list, "无数据");
        //easyExcel底层单sheet限制2万条
        Assert.isTrue(list.size() < 20_000, "导出只支持2万条以内数据,请细化筛选条件");
        ExportSheetConfig sheetConfig = new ExportSheetConfig() {{
            setData(list);
            setSheetName("sheet1");
        }};
       DownloadUtil.downloadExcel(fileName, outputStream -> writeExcel(outputStream, sheetConfig));
    }


    public static <T> void writeExcel(String filePath, ExportSheetConfig... exportConfig) {
        // 如果这里想使用03 则 传入excelType参数即可
        @Cleanup ExcelWriter excelWriter = EasyExcel.write(filePath).build();
        writeExcel(excelWriter, exportConfig);

    }

    public static <T> void writeExcel(OutputStream outputStream, ExportSheetConfig... exportConfig) {
        // 如果这里想使用03 则 传入excelType参数即可
        @Cleanup ExcelWriter excelWriter = EasyExcel.write(outputStream).build();
        writeExcel(excelWriter, exportConfig);

    }

    public static <T> void writeExcel(File file, ExportSheetConfig... exportConfig) {
        // 如果这里想使用03 则 传入excelType参数即可
        @Cleanup ExcelWriter excelWriter = EasyExcel.write(file).build();
        writeExcel(excelWriter, exportConfig);

    }

    private static void writeExcel(ExcelWriter excelWriter, ExportSheetConfig[] exportSheetConfigs
    ) {
        Stream.iterate(0, (i) -> ++i).limit(exportSheetConfigs.length).forEach(i -> {
            ExportSheetConfig exportSheetConfig = exportSheetConfigs[i];
            String sheetName = exportSheetConfigs[i].getSheetName();
            Boolean automaticallyMergeCells = exportSheetConfig.getAutomaticallyMergeCells();
            List<?> data = exportSheetConfig.getData();
            if (CollUtil.isNotEmpty(data)) {
                Class<?> aClass = data.get(0).getClass();
                ExcelWriterSheetBuilder excelWriterSheetBuilder = EasyExcel.writerSheet(i, sheetName)
                        .useDefaultStyle(exportSheetConfig.getUseDefaultStyle())            // 取消导出Excel的默认风格
                        //自动清理空值
                        .autoTrim(exportSheetConfig.getAutoTrim())
                        //自动合并表头
                        .automaticMergeHead(exportSheetConfig.getAutomaticMergeHead())
                        .head(aClass);

                if (automaticallyMergeCells) {
                    /**
                     * 自动合并单元格策略
                     */
                    buildAutoMerge(data, excelWriterSheetBuilder);
                }
                /**  自适应列宽*/
                if (exportSheetConfig.getAdaptiveColumnWidth()) {
                    excelWriterSheetBuilder.registerWriteHandler(new ExcelWidthStyleStrategy());
                }
                if (exportSheetConfig.getIsCenter()) {
/**  自定义样式单元格水平垂直居中*/
                    excelWriterSheetBuilder.registerWriteHandler(new CustomCellStyleStrategy());
                }


                WriteSheet writeSheet = excelWriterSheetBuilder.build();
                excelWriter.write(data, writeSheet);
            }
        });
    }

    /**
     * 自动合并单元格策略
     */
    public static void buildAutoMerge(List<?> data, ExcelWriterSheetBuilder excelWriterSheetBuilder) {
        //自动合并策略
        List<OnceAbsoluteMergeStrategy> onceAbsoluteMergeStrategy = getOnceAbsoluteMergeStrategy(data);
        if (CollUtil.isNotEmpty(onceAbsoluteMergeStrategy)) {
            onceAbsoluteMergeStrategy.forEach(excelWriterSheetBuilder::registerWriteHandler);
        }

        excelWriterSheetBuilder.registerWriteHandler(new HorizontalCellStyleStrategy());
    }

    public static <T> void writeExcel(String filePath, List<?> data) {
        writeExcel(filePath, "sheet1", data);
    }

    public static <T> void writeExcel(String filePath, String sheetName, List<?> data) {
        ExportSheetConfig exportSheetConfig = new ExportSheetConfig() {{
            setSheetName(sheetName);
            setData(data);
        }};
        writeExcel(filePath, exportSheetConfig);
    }

    public static <T> List<OnceAbsoluteMergeStrategy> getOnceAbsoluteMergeStrategy(List<T> list) {
        List<Range> mergeRanges = MergeRangeFinder.findMergeRanges(list);
        List<OnceAbsoluteMergeStrategy> collect = mergeRanges.stream().map(e -> new OnceAbsoluteMergeStrategy(e.getStartRow(), e.getEndRow(), e.getStartCol(), e.getEndCol())).collect(Collectors.toList());
        return collect;
    }

    @Autowired
    @SuppressWarnings("all")
    public void setHttpServletRequest(HttpServletRequest httpServletRequest) {
        this.httpServletRequest = httpServletRequest;
    }

    @Autowired
    @SuppressWarnings("all")
    public void setResponse(HttpServletResponse response) {
        EasyExcelWriteUtil.response = response;
    }


}
